package com.zf.easyexcel.utils.handler;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.*;
import java.util.stream.Collectors;


/**
 * 单元格合并的处理器
 */
public class CustomCellMergeWriteHandler implements CellWriteHandler {

    /**
     * 所有要合并的单元格的范围: 合并范围的右下角单元格行 -> 合并范围的右下角单元格列 -> 合并的范围
     */
    private Map<Integer, Map<Integer, CellRangeAddress>> mergeCellRangeAddressMap = new HashMap<>();


    /**
     * 传入 要合并的单元格的范围 列表
     */
    public CustomCellMergeWriteHandler(List<CellRangeAddress> mergeCellRangeAddressList) {
        if (mergeCellRangeAddressList == null || mergeCellRangeAddressList.isEmpty()) {
            return;
        }

        // 将合并单元范围转换成map: 合并范围的右下角单元格行 -> 合并范围的右下角单元格列 -> 合并的范围
        this.mergeCellRangeAddressMap =
                mergeCellRangeAddressList.stream()
                        .filter(item -> !(item.getFirstRow() == item.getLastRow() && item.getFirstColumn() == item.getLastColumn())) // 忽略只有一个单元的合并
                        .collect(
                                Collectors.groupingBy(
                                        CellRangeAddress::getLastRow,
                                        Collectors.toMap(CellRangeAddress::getLastColumn, t -> t, (old, cur) -> cur)
                                )
                        );
    }


    /**
     * 每个的单元格的所有操作完成后，都会执行该方法；
     * 下面两个方法重写任何一个都可以
     */
    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        afterCellDispose(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getCellDataList(),
                context.getCell(), context.getHeadData(), context.getRelativeRowIndex(), context.getHead());
    }

    /**
     * 每个的单元格的所有操作完成后，都会执行该方法
     *
     * @param writeSheetHolder 当前的sheet
     * @param writeTableHolder 使用表格(table)向sheet中写入数据时才有值；其他情况下值是NULL
     * @param cellDataList     1、在写入表头行的单元格的时候是NULL（但是从目前看一直都有值）； 2、写入数据行的单元格的时候一定有值；3、当使用FILL操作时可能有多个值；
     * @param cell             单元格的值
     * @param isHead           是否是头行里的单元格，使用Fill操作填充数据时是false
     * @param head             1、是头行时有值；2、非头行则为NULL、使用Fill操作填充数据时是NULL
     * @param relativeRowIndex 相对行位置，从0开始：
     *                         1、填充操作是NULL；
     *                         2、当写入的单元格是头行时，值是 （当前行的位置 - 第一行头行的位置），从0开始
     *                         3、当写入的单元格是数据行时，值是 （当前行的位置 - 第一行数据行的位置），从0开始
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List<WriteCellData<?>> cellDataList, Cell cell,
                                 Head head, Integer relativeRowIndex, Boolean isHead) {
        // 如果是头 或者 合并信息为空，则不做任何操作
        if (isHead || mergeCellRangeAddressMap.isEmpty()) {
            return;
        }

        // 数据行
        int rowIndex = cell.getRowIndex();
        int columnIndex = cell.getColumnIndex();
        Sheet sheet = writeSheetHolder.getSheet();

        // 如果是合并单元格的右下角，则执行合并操作
        CellRangeAddress cellAddresses = Optional.ofNullable(mergeCellRangeAddressMap.get(rowIndex))
                .orElse(Collections.emptyMap()).get(columnIndex);

        if (Objects.nonNull(cellAddresses)) {
            sheet.addMergedRegionUnsafe(cellAddresses);
        }
    }

    public static void main(String[] args) {
        ArrayList<List<String>> headList = new ArrayList<>();
        // 多行表头，自动合并
        headList.add(Arrays.asList("头1", "子1"));
        headList.add(Arrays.asList("头1", "子2"));
        headList.add(Arrays.asList("头2", "头2"));

        // 数据行
        ArrayList<List<String>> dataList = new ArrayList<>();
        dataList.add(Arrays.asList("r1-co1", "r1-co2", "r1-co3"));
        dataList.add(Collections.emptyList()); // 空行
        dataList.add(null); // 空行
        dataList.add(Arrays.asList("r2-co1", "r2-co2", "r2-co3"));


        // 合并单元格信息
        List<CellRangeAddress> cellRangeAddressList = Arrays.asList(
                new CellRangeAddress(2, 2, 0, 0),
                new CellRangeAddress(2, 2, 1, 2),
                new CellRangeAddress(5, 5, 0, 2)
        );


        // 写一个sheet
        EasyExcel.write("D:\\test.xlsx")
                .sheet(0)
                .head(headList)// 表头
                .registerWriteHandler(new CustomCellMergeWriteHandler(cellRangeAddressList)) // 注册写入处理器
                .doWrite(dataList);
    }
}
